package com.azier.kapacitor.util;

import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.*;

/**
 * HTTP 请求工具类, 请设置 java 参数: -Djava.net.preferIPv4Stack=true
 *
 * @author 金建强(ptma@163.com)
 * @version 2016/6/15 15:05
 */
public class HttpUtil {

    private static PoolingHttpClientConnectionManager connectionManager = null;
    static {
        connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(1024);
        connectionManager.setDefaultMaxPerRoute(128);
    }

    private static HttpClient createClient() {
        // HttpClients.createDefault(); //如果不采用连接池就是这种方式获取连接
        return HttpClientBuilder.create().setConnectionManager(connectionManager).build();
    }
    /**
     * GET 请求
     *
     * @param url 请求 Url
     * @return 请求结果
     * @throws IOException
     */
    public static String get(String url) throws IOException {
        return get(url, null, null);
    }

    /**
     * GET 请求
     *
     * @param url 请求 Url
     * @param params 参数
     * @return 请求结果
     * @throws IOException
     */
    public static String get(String url, Map<String, Object> params) throws IOException {
        return get(url, params, null);
    }

    /**
     * POST 表单 (Content-Type: application/x-www-form-urlencoded; charset=UTF-8)
     *
     * @param url 请求 Url
     * @param params Map with parameters/values
     * @param headers HTTP头
     * @return 请求结果
     * @throws IOException
     */
    public static String get(String url, Map<String, Object> params, Map<String, String> headers) throws IOException {
        HttpClient client = createClient();
        HttpGet request = new HttpGet(appendQueryParams(url, params));

        // headers
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
            headers.put(HttpHeaders.ACCEPT_ENCODING, "gzip");
        }
        Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            request.addHeader(entry.getKey(), entry.getValue());
        }

        HttpResponse response = client.execute(request);
        return responseToString(response);
    }

    /**
     * GET 请求 (Accept:application/json)
     *
     * @param url 请求 Url
     * @return 请求结果
     * @throws IOException
     */
    public static String getJson(String url) throws IOException {
        return getJson(url, null, null);
    }

    /**
     * GET 请求 (Accept:application/json)
     *
     * @param url 请求 Url
     * @param params 参数
     * @return 请求结果
     * @throws IOException
     */
    public static String getJson(String url, Map<String, Object> params) throws IOException {
        return getJson(url, params, null);
    }

    /**
     * GET 请求 (Accept:application/json)
     *
     * @param url 请求 Url
     * @param params 参数
     * @param headers HTTP头
     * @return 请求结果
     * @throws IOException
     */
    public static String getJson(String url, Map<String, Object> params, Map<String, String> headers)
            throws IOException {
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        headers.put(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        return get(url, params, headers);
    }

    /**
     * POST 表单 (Content-Type: application/x-www-form-urlencoded; charset=UTF-8)
     *
     * @param url 请求 Url
     * @param params map with parameters/values
     * @return 请求结果
     * @throws IOException
     */
    public static String postForm(String url, Map<String, Object> params) throws IOException {
        return postForm(url, params, null);
    }

    /**
     * POST 表单 (Content-Type: application/x-www-form-urlencoded; charset=UTF-8)
     *
     * @param url 请求 Url
     * @param params Map with parameters/values
     * @param headers HTTP头
     * @return 请求结果
     * @throws IOException
     */
    public static String postForm(String url, Map<String, Object> params, Map<String, String> headers)
            throws IOException {
        HttpClient client = createClient();
        HttpPost request = new HttpPost(url);

        // headers
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
            headers.put(HttpHeaders.ACCEPT_ENCODING, "gzip");
        }
        Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            request.addHeader(entry.getKey(), entry.getValue());
        }

        // parameters
        if (params != null && !params.isEmpty()) {
            List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
            Iterator<Map.Entry<String, Object>> itr = params.entrySet().iterator();
            while (itr.hasNext()) {
                Map.Entry<String, Object> entry = itr.next();
                urlParameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
            }
            request.setEntity(new UrlEncodedFormEntity(urlParameters, Charset.forName("UTF-8")));
        }

        HttpResponse response = client.execute(request);
        return responseToString(response);
    }

    /**
     * POST 请求
     *
     * @param url 请求 Url
     * @param body 请求正文
     * @return 请求结果
     * @throws IOException
     */
    public static String postBody(String url, String body) throws IOException {
        return postBody(url, body, null);
    }

    /**
     * POST 请求
     *
     * @param url 请求 Url
     * @param body 请求正文
     * @param headers HTTP头
     * @return 请求结果
     * @throws IOException
     */
    public static String postBody(String url, String body, Map<String, String> headers) throws IOException {
        HttpClient client = createClient();
        HttpPost request = new HttpPost(url);

        // headers
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
            headers.put(HttpHeaders.ACCEPT_ENCODING, "gzip");
        }
        Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            request.setHeader(entry.getKey(), entry.getValue());
        }

        // body
        StringEntity data = new StringEntity(body, Charset.forName("UTF-8"));
        request.setEntity(data);

        HttpResponse response = client.execute(request);
        return responseToString(response);
    }

    /**
     * POST 请求
     *
     * @param url 请求 Url
     * @param jsonObj 请求正文
     * @return 请求结果
     * @throws IOException
     */
    public static String postJson(String url, Object jsonObj) throws IOException {
        return postJson(url, jsonObj, null);
    }

    /**
     * POST 请求
     *
     * @param url 请求 Url
     * @param jsonObj 请求json对象
     * @param headers HTTP头
     * @return 请求结果
     * @throws IOException
     */
    public static String postJson(String url, Object jsonObj, Map<String, String> headers) throws IOException {
        HttpClient client = createClient();
        HttpPost request = new HttpPost(url);

        // headers
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        if (!headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
            headers.put(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        }
        if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
            headers.put(HttpHeaders.ACCEPT_ENCODING, "gzip");
        }
        Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            request.addHeader(entry.getKey(), entry.getValue());
        }

        // body
        request.setEntity(EntityBuilder.create().setContentType(ContentType.APPLICATION_JSON).setText(JsonUtil.toJson(jsonObj)).build());

        HttpResponse response = client.execute(request);
        return responseToString(response);
    }

    /**
     * PUT 请求
     *
     * @param url 请求 Url
     * @return 请求结果
     * @throws IOException
     */
    public static String put(String url, String body, Map<String, String> headers) throws IOException {
        HttpClient client = createClient();
        HttpPut request = new HttpPut(url);

        // headers
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
            headers.put(HttpHeaders.ACCEPT_ENCODING, "gzip");
        }
        Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            request.addHeader(entry.getKey(), entry.getValue());
        }

        // body
        request.setEntity(EntityBuilder.create().setContentType(ContentType.create("text/plain", "UTF-8")).setText(body).build());

        HttpResponse response = client.execute(request);
        return responseToString(response);
    }

    /**
     * PATCH 请求
     *
     * @param url 请求 Url
     * @return 请求结果
     * @throws IOException
     */
    public static String patch(String url, String body, Map<String, String> headers) throws IOException {
        HttpClient client = createClient();
        HttpPatch request = new HttpPatch(url);

        // headers
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
            headers.put(HttpHeaders.ACCEPT_ENCODING, "gzip");
        }
        Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            request.addHeader(entry.getKey(), entry.getValue());
        }

        // body
        request.setEntity(EntityBuilder.create().setContentType(ContentType.create("text/plain", "UTF-8")).setText(body).build());

        HttpResponse response = client.execute(request);
        return responseToString(response);
    }

    /**
     * PATCH 请求
     *
     * @param url 请求 Url
     * @return 请求结果
     * @throws IOException
     */
    public static String patchJson(String url, Object jsonObj) throws IOException {
        return patchJson(url, jsonObj, null);
    }

    /**
     * PATCH 请求
     *
     * @param url 请求 Url
     * @return 请求结果
     * @throws IOException
     */
    public static String patchJson(String url, Object jsonObj, Map<String, String> headers) throws IOException {
        HttpClient client = createClient();
        HttpPatch request = new HttpPatch(url);

        // headers
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
            headers.put(HttpHeaders.ACCEPT_ENCODING, "gzip");
        }
        Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            request.addHeader(entry.getKey(), entry.getValue());
        }

        // body
        request.setEntity(EntityBuilder.create().setContentType(ContentType.create("text/plain", "UTF-8")).setText(JsonUtil.toJson(jsonObj)).build());

        HttpResponse response = client.execute(request);
        return responseToString(response);
    }
    /**
     * DELETE 请求
     *
     * @param url 请求 Url
     * @return 请求结果
     * @throws IOException
     */
    public static String delete(String url) throws IOException {
        return delete(url, null);
    }

    /**
     * DELETE 请求
     *
     * @param url 请求 Url
     * @param headers HTTP头
     * @return 请求结果
     * @throws IOException
     */
    public static String delete(String url, Map<String, String> headers) throws IOException {
        HttpClient client = createClient();
        HttpDelete request = new HttpDelete(url);

        // headers
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
            headers.put(HttpHeaders.ACCEPT_ENCODING, "gzip");
        }
        Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            request.addHeader(entry.getKey(), entry.getValue());
        }

        HttpResponse response = client.execute(request);
        return responseToString(response);
    }

    /**
     * Post 上传
     *
     * @param url
     * @param params
     * @param files
     * @return
     */
    public static String postMultiPart(String url, Map<String, Object> params, Collection<FileEntry> files) throws IOException {
        return postMultiPart(url, params, files, null);
    }

    /**
     * Post 上传
     *
     * @param url
     * @param params
     * @param files
     * @param headers
     * @return
     */
    public static String postMultiPart(String url, Map<String, Object> params, Collection<FileEntry> files,
            Map<String, String> headers) throws IOException {
        HttpClient client = createClient();
        HttpPost request = new HttpPost(url);
        // headers
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
            headers.put(HttpHeaders.ACCEPT_ENCODING, "gzip");
        }
        Iterator<Map.Entry<String, String>> iterator = headers.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            request.addHeader(entry.getKey(), entry.getValue());
        }

        // setup multipart entity
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

        // parameters
        if (params != null) {
            List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                if (entry.getValue() instanceof File) {
                    File f = (File) entry.getValue();
                    FileBody fileBody = new FileBody(f);
                    builder.addPart(entry.getKey(), fileBody);
                    // identify param type by Key
                } else {
                    builder.addPart(entry.getKey(), new StringBody(String.valueOf(entry.getValue()),
                            ContentType.create("text/plain", "UTF-8")));
                }
            }
        }

        if (files != null) {
            for (FileEntry entry : files) {
                InputStreamBody fileBody =
                        new InputStreamBody(entry.getStream(), entry.getContentType(), entry.getFileName());
                builder.addPart(entry.getName(), fileBody);
            }
        }

        request.setEntity(builder.build());

        HttpResponse response = client.execute(request);
        return responseToString(response);
    }

    /**
     * WebService SOAP1.1 请求
     *
     * @param wsdl WebService地址或 wsdl 地址
     * @param namespace 命名空间
     * @param method 方法名
     * @param params 参数, 为了保证参数有序, 建议使用 LinkedHashMap
     * @return 请求结果
     * @throws Exception
     */
    public static String soap11(String wsdl, String namespace, String method, Map<String, Object> params)
            throws Exception {
        // 拼接SOAP
        StringBuffer soapRequestData = new StringBuffer("");
        soapRequestData.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"");
        if (StringUtil.isNotEmpty(namespace)) {
            soapRequestData.append(StringUtil.format(" xmlns:ws=\"{}\"", namespace));
        }
        soapRequestData.append(">");
        soapRequestData.append("<soapenv:Header/>");
        soapRequestData.append("<soapenv:Body>");
        if (StringUtil.isEmpty(namespace)) {
            soapRequestData.append(StringUtil.format("<{}>", method));
        } else {
            soapRequestData.append(StringUtil.format("<ws:{}>", method));
        }

        // 拼接参数
        soapRequestData.append(getSoapMethodParam(params));

        if (StringUtil.isEmpty(namespace)) {
            soapRequestData.append(StringUtil.format("</{}>", method));
        } else {
            soapRequestData.append(StringUtil.format("</ws:{}>", method));
        }

        soapRequestData.append("</soapenv:Body></soapenv:Envelope>");

        // SOAP 1.1 HTTP 头
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("Content-Type", "text/xml; charset=UTF-8");
        headers.put("SOAPAction", method);

        return postBody(wsdl, soapRequestData.toString(), headers);
    }

    /**
     * WebService SOAP1.2 请求
     *
     * @param wsdl WebService地址或 wsdl 地址
     * @param namespace 命名空间
     * @param method 方法名
     * @param params 参数, 为了保证参数有序, 建议使用 LinkedHashMap
     * @return 请求结果
     * @throws Exception
     */
    public static String soap12(String wsdl, String namespace, String method, Map<String, Object> params)
            throws Exception {
        // 拼接SOAP
        StringBuffer soapRequestData = new StringBuffer("");
        soapRequestData.append("<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\"");
        if (StringUtil.isNotEmpty(namespace)) {
            soapRequestData.append(StringUtil.format(" xmlns:ws=\"{}\"", namespace));
        }
        soapRequestData.append(">");
        soapRequestData.append("<soap:Header/>");
        soapRequestData.append("<soap:Body>");
        if (StringUtil.isEmpty(namespace)) {
            soapRequestData.append(StringUtil.format("<{}>", method));
        } else {
            soapRequestData.append(StringUtil.format("<ws:{}>", method));
        }

        // 拼接参数
        soapRequestData.append(getSoapMethodParam(params));

        if (StringUtil.isEmpty(namespace)) {
            soapRequestData.append(StringUtil.format("</{}>", method));
        } else {
            soapRequestData.append(StringUtil.format("</ws:{}>", method));
        }

        soapRequestData.append("</soap:Body></soap:Envelope>");

        // SOAP 1.2 HTTP 头
        Map<String, String> headers = new HashMap<String, String>();
        headers.put(HttpHeaders.CONTENT_TYPE, "application/soap+xml;UTF-8");

        return postBody(wsdl, soapRequestData.toString(), headers);
    }


    /**
     * 下载文件
     *
     * @param url 请求 Url
     * @return
     * @throws IOException
     */
    public static InputStream download(String url) throws IOException {
        return download(url, null);
    }

    /**
     * 下载文件
     *
     * @param url 请求 Url
     * @param params 参数
     * @return
     * @throws IOException
     */
    public static InputStream download(String url, Map<String, Object> params) throws IOException {
        HttpClient client = createClient();
        HttpGet request = new HttpGet(appendQueryParams(url, params));
        HttpResponse response = client.execute(request);
        return response.getEntity().getContent();
    }

    private static String getSoapMethodParam(Map<String, Object> params) {
        StringBuffer param = new StringBuffer("");
        if (params != null && !params.isEmpty()) {
            Iterator<Map.Entry<String, Object>> itr = params.entrySet().iterator();
            while (itr.hasNext()) {
                Map.Entry<String, Object> entry = itr.next();
                param.append(StringUtil.format("<{}>{}</{}>", entry.getKey(), entry.getValue(), entry.getKey()));
            }
        }
        return param.toString();
    }


    /**
     * 拼接参数到 Url
     *
     * @param url 请求 Url
     * @param params 参数
     * @return url 拼装后的 Url
     * @throws IOException
     */
    public static String appendQueryParams(String url, Map<String, Object> params) throws IOException {
        StringBuffer fullUrl = new StringBuffer(url);
        if (params != null) {
            boolean first = (fullUrl.indexOf("?") == -1);
            Iterator<Map.Entry<String, Object>> itr = params.entrySet().iterator();
            while (itr.hasNext()) {
                Map.Entry<String, Object> entry = itr.next();
                if (first) {
                    fullUrl.append("?");
                    first = false;
                } else {
                    fullUrl.append("&");
                }
                fullUrl.append(URLEncoder.encode(entry.getKey(), "UTF-8")).append("=");
                fullUrl.append(URLEncoder.encode(entry.getValue().toString(), "UTF-8"));
            }
        }

        return fullUrl.toString();
    }

    private static String responseToString(HttpResponse response) throws IOException {
        HttpEntity entity = response.getEntity();
        Header contentEncodingHeader = entity.getContentEncoding();

        if (contentEncodingHeader != null) {
            HeaderElement[] encodings = contentEncodingHeader.getElements();
            for (int i = 0; i < encodings.length; i++) {
                if ("gzip".equalsIgnoreCase(encodings[i].getName())) {
                    entity = new GzipDecompressingEntity(entity);
                    break;
                }
            }
        }

        return EntityUtils.toString(entity, Charset.forName("UTF-8").name());
    }


    public static class FileEntry {
        private String      name;
        private String      fileName;
        private ContentType contentType;
        private InputStream stream;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        public ContentType getContentType() {
            return contentType;
        }

        public void setContentType(ContentType contentType) {
            this.contentType = contentType;
        }

        public InputStream getStream() {
            return stream;
        }

        public void setStream(InputStream stream) {
            this.stream = stream;
        }
    }
}
